﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using Red.Controls;
using Red.Controls.Depot;
using Red.Controls.Shell;
using Red.Controls.Utils;
using Red.Controls.ViewModels;
using Red.Core;
using Red.Core.Extensions;
using Red.Core.GoogleAnalytics;
using SourceControl = Red.Core.SourceControl;

namespace Red.Toolkit.AssetBrowser
{
    [Export( typeof( IEditorInfo ) )]
    public class EditorInfo : IEditorInfo
    {
        public string Name
        {
            get { return "Asset Browser"; }
        }

        public Type WindowClass
        {
            get { return typeof( AssetBrowser ); }
        }

        public Type SettingsClass 
        { 
            get { return typeof( UserSettings ); }
        }
    }
    
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class AssetBrowser : RedEditorWindow, IResourceBrowser
    {
        #region Data

        private readonly List<string> _errors = new List<string>();
        private readonly RedApplication _app;
        private readonly DepotInfo _depot;
        private AppMonitorWindow _appMonitorWindow;

        #endregion Data

        public SourceControl.Monitor Monitor { get; private set; } = new SourceControl.Monitor();

        #region C-tor

        public AssetBrowser( IEditorHost host, EditorInstanceData instanceData, EditorSharedData sharedData )
            : base( host, instanceData, sharedData )
        {
            _app = host as RedApplication; // a little bit hacky, needed to switch app themes, may be removed when the Launcher takes all the resp. for it
            Debug.Assert( _app != null );
            _depot = sharedData.DepotInfo;
            
            Monitor.SourceControl = _depot.SourceControl;
            Monitor.Error += P4Mediator_Error;
            Closed += ( object sender, EventArgs e ) => Monitor.UnregisterListeners();

            InitializeComponent();
            _browser.ShowAssetView = InstanceData.AdditionalParams.ContainsKey( "useAssetView" );
            _browser.Depot = _depot;

            SharedData.EngineMonitor.OnCrash += ( string log ) =>
            {
                RedApplication.Current.Dispatcher.Invoke( DispatcherPriority.Background, new Action(
                () =>
                {
                    InstanceData.NotifyDispatcher.NotifyError( "Engine crashed" );
                } ) );
            };

            SharedData.EngineMonitor.OnConnectionLost += () =>
            {
                RedApplication.Current.Dispatcher.Invoke( DispatcherPriority.Background, new Action(
                () =>
                {
                    InstanceData.NotifyDispatcher.NotifyError( "Engine connection lost" );
                } ) );
            };
        }

        #endregion C-tor

        #region Event handlers

        private async void P4Mediator_Error( string message )
        {
            if ( _errors.Contains( message ) )
                return;

            _errors.Add( message );
            await ShowErrorAsync( message );
        }
        
        private async void RestoreEngine_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            await SharedData.EngineApp.Restore();
            SharedData.EngineMonitor.Enabled = true;
        }

        private void ShowEngineMonitor_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            if ( _appMonitorWindow == null )
            {
                AppMonitorViewModel appMonitorViewModel = new AppMonitorViewModel( SharedData.EngineMonitor );
                _appMonitorWindow = new AppMonitorWindow();
                _appMonitorWindow.DataContext = appMonitorViewModel;
                _appMonitorWindow.Title = "Engine Monitor";
                _appMonitorWindow.Closed += ( object closedSender, EventArgs closedEventArgs ) =>
                {
                    appMonitorViewModel.MonitoringEnabled = false;
                    _appMonitorWindow = null;
                };
                _appMonitorWindow.Show();
            }
            else
            {
                if ( _appMonitorWindow.WindowState == WindowState.Minimized )
                    _appMonitorWindow.WindowState = WindowState.Normal;
                _appMonitorWindow.Focus();
            }
        }
        
        private async void ImportResource_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            EventHandler<ResourceFactory.Importer> onImporterSelected = async ( object s, ResourceFactory.Importer importer ) =>
            {
                var dialog = new Microsoft.Win32.OpenFileDialog
                {
                    //FileName = Path.GetFileName( param.Value ),
                    InitialDirectory = SharedData.DepotInfo.RootPath,
                    Multiselect = false,
                    Filter = @"All resources (*.*)|*.*",
                    Title = @"Select source file"
                };

                if ( true.Equals( dialog.ShowDialog() ) )
                {
                    string destPath = e.Parameter as string;
                    Result result = await SharedData.ResourceFactory.Import( dialog.FileName, destPath, importer );
                    if ( !result )
                    {
                        await ShowErrorAsync( result.Message );
                    }
                    // refreshing view could be in else statement
                    InstanceData.NotifyDispatcher.NotifyInformation( "Resource imported" );     // TODO: probably there's a better place for it.
                    await _browser.RefreshView();
                }
            };

            List<ResourceFactory.Importer> importers = await SharedData.ResourceFactory.Importers;

            var importersList = new List<CommandPaletteItem>();
            foreach ( ResourceFactory.Importer importer in importers )
            {
                if ( !string.IsNullOrEmpty( importer.ClassName ) )
                {
                    var item = new CommandPaletteItem<ResourceFactory.Importer>( importer ) { Name = importer.ClassName };
                    item.Click += onImporterSelected;
                    importersList.Add( item );
                }
            }

            CommandPalette importerSelector = new CommandPalette() { Owner = this, Items = importersList };
            importerSelector.ResetInitialState();
            var mousePos = PointToScreen( Mouse.GetPosition( this ) );
            importerSelector.Top = mousePos.Y;
            importerSelector.Left = mousePos.X;
            importerSelector.Show();
        }

        private async void CreateResource_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            string resourceName = TextInputDialog.Show( this, "New resource", "Resource name", "Create new resource" );

            if ( string.IsNullOrEmpty( resourceName ) )
            {
                return;
            }

            if ( HasInvalidPathChars( resourceName ) )
            {
                return;
            }

            EventHandler<ResourceFactory.Factory> onFactorySelected = async ( object s, ResourceFactory.Factory factory ) =>
            {
                string destPath = e.Parameter as string;
                string baseName = Path.GetFileNameWithoutExtension( resourceName );
                string fullPath = Path.Combine( destPath, baseName ) + "." + factory.Extension;
                Result result = await SharedData.ResourceFactory.Create(fullPath);
                if ( result )
                {
                    InstanceData.NotifyDispatcher.NotifyInformation( "Resource created" );
                    await _browser.RefreshView();
                }
                else
                {
                    await ShowErrorAsync( result.Message );
                }
            };

            List<ResourceFactory.Factory> factories = await SharedData.ResourceFactory.Factories;

            var factoriesList = new List<CommandPaletteItem>();
            foreach ( ResourceFactory.Factory factory in factories )
            {
                if ( !string.IsNullOrEmpty( factory.ClassName ) )
                {
                    var item = new CommandPaletteItem<ResourceFactory.Factory>( factory ) { Name = factory.ClassName };
                    item.Click += onFactorySelected;
                    factoriesList.Add( item );
                }
            }

            CommandPalette importerSelector = new CommandPalette() { Owner = this, Items = factoriesList };
            importerSelector.ResetInitialState();
            var mousePos = PointToScreen( Mouse.GetPosition( this ) );
            importerSelector.Top = mousePos.Y;
            importerSelector.Left = mousePos.X;
            importerSelector.Show();
        }

        private void CreateFolder_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            var folderName = TextInputDialog.Show( this, "New folder", "Folder name", "Create folder" );

            if ( string.IsNullOrEmpty( folderName ) )
            {
                return;
            }

            if ( HasInvalidPathChars( folderName ) )
            {
                return;
            }

            string destPath = e.Parameter as string;
            string newFolderPath = Path.Combine( destPath, folderName );
            Directory.CreateDirectory( newFolderPath );
        }

        private void Browser_ResourceDoubleClick( object sender, DepotBrowser.ResourceDoubleClickEventArgs e )
        {
            Host.EditResourceAsync( e.Item.Path, this ).DoNotAwait();
        }

        #endregion Event handlers

        #region RedMainWindow implementation

        protected override void WriteToSettings( RedSettings settings )
        {
            var userSettings = (UserSettings)settings;
            userSettings.Favorites = _browser.Favorites.ToArray();
            userSettings.Tabs = _browser.Tabs.ToArray();
            userSettings.ActiveTabIndex = _browser.ActiveTabIndex;
        }

        protected override void LoadFromSettings( RedSettings settings )
        {
            var userSettings = (UserSettings)settings;
            _browser.Favorites = userSettings.Favorites;
            _browser.LoadTabs( userSettings.Tabs, userSettings.ActiveTabIndex );
        }

        protected override GoogleAnalyticsContext GetGoogleAnalyticsContext()
        {
            return new GoogleAnalyticsContext( "Asset Browser", "UA-42984990-7" );
        }

        #endregion RedMainWindow implementation

        #region Private methods
        
        private bool HasInvalidPathChars( string path )
        {
            var invalidPathChars = Path.GetInvalidPathChars().ToList();
            return path.Any( pathChar => invalidPathChars.Contains( pathChar ) );
        }

        #endregion Private methods

        #region IResourceBrowser implementation
        
        public void NavigateToResource( string resourcePath )
        {
            _browser.SelectByPath( resourcePath );
        }

        #endregion IResourceBrowser implementation
    }
}
